package com.tchepannou.limbe.helper;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

public class ClassHelper
{
    public static List<Class<?>> loadClasses(String packageName, ClassHelper.Filter filter) 
        throws ClassNotFoundException, IOException
    {
        /* all directories */
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements())
        {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }

        /* Load the classes */
        ArrayList<Class<?>> classes = new ArrayList<Class<?>>();
        for (File directory : dirs)
        {
            classes.addAll(loadClasses(directory, packageName, filter));
        }
        return classes;
    }

    /**
     * Recursive method used to find all classes in a given directory and
     * subdirs.
     * 
     * @param directory
     *            The base directory
     * @param packageName
     *            The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    private static List<Class<?>> loadClasses(File directory, String packageName, ClassHelper.Filter filter) 
        throws ClassNotFoundException
    {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        if (!directory.exists())
        {
            return classes;
        }

        File[] files = directory.listFiles();
        for (File file : files)
        {
            String classname = packageName + '.'
                    + file.getName().substring(0, file.getName().length() - 6);
            Class<?> clazz = Class.forName(classname);

            if (filter.accept(clazz))
            {
                classes.add(clazz);
            }

        }
        return classes;
    }

    // -- Static Classes
    public interface Filter
    {
        public boolean accept(Class<?> clazz);
    }
}
